/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package br.com.felinelayer.server.application;

import br.com.felinelayer.common.Message;
import br.com.felinelayer.common.Protocol;
import java.util.ArrayList;
import java.util.HashMap;

/**
 *
 * @author Vanius Roberto Bittencourt
 */
public abstract class ComponentServer<T extends ComponentServer> {

    private static final int PIXELS_PER_ROW = 28;
    private static final int PIXELS_PER_COL = 28;
    private int left = 0;
    private int top = 0;
    private int width = 0;
    private int height = 0;
    private int id = 0;
    private String text = "";
    private boolean updateFromOtherSide = false;
    private HashMap<String, ArrayList<IEventListenerServer>> eventListeners = new HashMap<>();
    private HashMap<String, ArrayList<IEventListenerServerCanDo>> eventListenersCanDo = new HashMap<>();
    private Application application;
    private FrameServer frame;

    protected Message inquireMessage( Message message ) {
        message.setValueByName( Protocol.COMPONENT_ID, getId() );
        return application.inquire( message );
    }

    protected T sendMessage( Message message ) {
        message.setValueByName( Protocol.COMPONENT_ID, getId() );
        application.sendMessage( message );
        return (T) this;
    }

    protected void firePropertyChange( String propertyName, int value ) {
        firePropertyChange( propertyName, value + "" );
    }

    protected void firePropertyChange( String propertyName, String value ) {
        Message message = new Message( Protocol.MSG_NAME_COMPONENT_PROPERTY_CHANGE );
        message.setValueByName( Protocol.COMPONENT_ID, getId() );
        message.setValueByName( propertyName, value );
        sendMessage( message );
    }

    protected void doMessageReceived( Message message ) {
    }

    public boolean isUpdateFromOtherSide() {
        return updateFromOtherSide;
    }

    public T setUpdateFromOtherSide( boolean updateFromOtherSide ) {
        this.updateFromOtherSide = updateFromOtherSide;
        return (T) this;
    }

    public T setText( String text ) {
        this.text = text;
        if ( !updateFromOtherSide ) {
            firePropertyChange( Protocol.PROPERTY_NAME_TEXT, text );
        }
        return (T) this;
    }

    public String getText() {
        return text;
    }

    public void setFrame( FrameServer frame ) {
        this.frame = frame;
    }

    public T setRow( int row ) {
        setTop( (row + 1) * PIXELS_PER_ROW );
        return (T) this;
    }

    public T setCol( int col ) {
        setLeft( (col + 1) * PIXELS_PER_COL );
        return (T) this;
    }

    public int getLeft() {
        return left;
    }

    public T setLeft( int left ) {
        this.left = left;
        firePropertyChange( Protocol.PROPERTY_NAME_LEFT, left + "" );
        return (T) this;
    }

    public int getTop() {
        return top;
    }

    public T setTop( int top ) {
        this.top = top;
        firePropertyChange( Protocol.PROPERTY_NAME_TOP, top + "" );
        return (T) this;
    }

    public int getWidth() {
        return width;
    }

    public T setWidth( int width ) {
        this.width = width;
        firePropertyChange( Protocol.PROPERTY_NAME_WIDTH, width + "" );
        return (T) this;
    }

    public int getHeight() {
        return height;
    }

    public T setHeight( int height ) {
        this.height = height;
        firePropertyChange( Protocol.PROPERTY_NAME_HEIGHT, height + "" );
        return (T) this;
    }

    /**
     * Returns component id number
     *
     * @return int
     */
    public int getId() {
        return id;
    }

    /**
     * Defines component id number
     *
     * @param id
     */
    public T setId( int id ) {
        this.id = id;
        return (T) this;
    }

    public T addEventListener( String eventName, IEventListenerServer eventListener ) {
        ArrayList<IEventListenerServer> l = eventListeners.get( eventName );
        if ( l == null ) {
            l = new ArrayList<>();
        }
        if ( l.indexOf( l ) < 0 ) {
            l.add( eventListener );
        }
        eventListeners.put( eventName, l );
        Message message = new Message( Protocol.EVENT_ADD_LISTENER );
        message.setValueByName( Protocol.EVENT_NAME, eventName );
        sendMessage( message );
        return (T) this;
    }

    public T addEventListener( String eventName, IEventListenerServerCanDo eventListener ) {
        ArrayList<IEventListenerServerCanDo> l = eventListenersCanDo.get( eventName );
        if ( l == null ) {
            l = new ArrayList<>();
        }
        if ( l.indexOf( l ) < 0 ) {
            l.add( eventListener );
        }
        eventListenersCanDo.put( eventName, l );
        Message message = new Message( Protocol.EVENT_ADD_LISTENER );
        message.setValueByName( Protocol.EVENT_NAME, eventName );
        sendMessage( message );
        return (T) this;
    }

    public T addOnClickListener( IEventListenerServer eventListener ) {
        return addEventListener( Protocol.EVENT_CLICK, eventListener );
    }

    public T addOnExitListener( IEventListenerServer eventListener ) {
        return addEventListener( Protocol.EVENT_FOCUS_EXIT, eventListener );
    }

    public T addOnEnterListener( IEventListenerServer eventListener ) {
        return addEventListener( Protocol.EVENT_FOCUS_ENTER, eventListener );
    }

    public T addOnBeforeCloseListener( IEventListenerServerCanDo eventListener ) {
        return addEventListener( Protocol.EVENT_BEFORE_CLOSE, eventListener );
    }

    public void messageReceived( Message message ) {
        doMessageReceived( message );
        if ( !message.isPropertyExists( Protocol.EVENT_NAME ) ) {
            return;
        }
        String eventName = message.getValueByName( Protocol.EVENT_NAME );
        ArrayList<IEventListenerServer> l = eventListeners.get( eventName );
        if ( l != null ) {
            for ( IEventListenerServer e : l ) {
                e.onEvent( this, eventName, message );
            }
        }
        ArrayList<IEventListenerServerCanDo> lc = eventListenersCanDo.get( eventName );
        if ( lc != null ) {
            for ( IEventListenerServerCanDo e : lc ) {
                boolean can = e.onEvent( this, eventName, message );
                if ( eventName.equals( Protocol.EVENT_BEFORE_CLOSE ) ) {
                    if ( can ) {
                        message.setValueByName( Protocol.EVENT_BEFORE_CLOSE_CANCLOSE, Protocol.YES );
                    } else {
                        message.setValueByName( Protocol.EVENT_BEFORE_CLOSE_CANCLOSE, Protocol.NO );
                    }
                }
            }
        }
    }

    public void clearListeners() {
        eventListeners.clear();
    }

    public void destroy() {
        Message m = new Message().setName( Protocol.MSG_NAME_DESTROY_COMPONENT );
        sendMessage( m );
    }

    public abstract String getComponentClassName();

    public T addLabelLeft( String text ) {
        frame.addLabel( text ).setAnchorLeft( this );
        return (T) this;
    }

    public T addLabelTop( String text ) {
        frame.addLabel( text ).setAnchorTop( this );
        return (T) this;
    }

    public T setApplication( Application application ) {
        this.application = application;
        return (T) this;
    }

    public Application getApplication() {
        return application;
    }

    public void initialize() {
    }
}
